package db.ibatis;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

@Intercepts(value = {
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})

public class PageInterceptor implements Interceptor {

    private static Logger logger = LoggerFactory.getLogger(PageInterceptor.class);

    private String dbType;

    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        Object parameter = null;

        // Get the id in mapper.xml
        String id = statement.getId();
        if (id.matches(".+ByPage$")) {
            MetaObject metaObject = SystemMetaObject.forObject(statement);
            DataSource dataSource = (DataSource) metaObject.getValue("configuration.environment.dataSource");
            Connection con = dataSource.getConnection();
            //String url = con.getMetaData().getURL();

            if (args.length > 1) {
                parameter = args[1];  // Object
            }
            BoundSql boundSql = statement.getBoundSql(parameter);
            // Get the jdbc sql
            String jdbcSql = boundSql.getSql();

            String countSql = "select count(1) from (" + jdbcSql + ") a";
            logger.info(countSql);

            PreparedStatement countStatement = con.prepareStatement(countSql);
            ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
            parameterHandler.setParameters(countStatement);
            ResultSet rs = countStatement.executeQuery();

            // Get the parameter from mapper parameter
            //Map<String,Object> parameter = (Map<String,Object>) boundSql.getParameterObject();
            Map<?, ?> param = (Map<?, ?>) boundSql.getParameterObject();
            Page page = (Page) param.get("page");
            if (page == null)
                System.out.println("page is null");

            if (rs.next()) {
                long cnt = rs.getLong(1);
                if (cnt < 1)
                    return null;
                else
                    page.setTotalNumber((int) cnt);
            }
            // Set the pager sql for mysql
            String pagerSql = jdbcSql + " limit " + page.getDbIndex() + "," + page.getPageSize();
            metaObject.setValue("delegate.boundSql.sql", pagerSql);
        }
        System.out.println("plugin executed successful.");
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        if (target instanceof Executor) {
            System.out.println("plugin starting...");
            return Plugin.wrap(target, this);
        } else
            return target;
    }


    public void setProperties(Properties properties) {
        this.dbType = properties.getProperty("dbType");
        System.out.println("set properties for ibatis plugin succeed .");
    }
}
